package org.springblade.modules.repair.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springblade.core.log.annotation.ApiLog;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.sms.SmsTemplate;
import org.springblade.core.sms.model.SmsCode;
import org.springblade.core.sms.model.SmsData;
import org.springblade.core.tenant.annotation.TenantIgnore;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.*;
import org.springblade.modules.auth.provider.ITokenGranter;
import org.springblade.modules.auth.provider.TokenGranterBuilder;
import org.springblade.modules.auth.provider.TokenParameter;
import org.springblade.modules.auth.utils.TokenUtil;
import org.springblade.modules.resource.builder.sms.SmsBuilder;
import org.springblade.modules.resource.utils.SmsUtil;
import org.springblade.modules.system.dto.PasswordDTO;
import org.springblade.modules.system.dto.PasswordUpdateDTO;
import org.springblade.modules.system.entity.User;
import org.springblade.modules.system.entity.UserInfo;
import org.springblade.modules.system.service.IUserService;
import org.springblade.modules.system.vo.AppUserTenantInfoVO;
import org.springblade.modules.system.vo.TenantInfoVO;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.time.Duration;
import java.util.List;
import java.util.Map;

import static org.springblade.core.sms.constant.SmsConstant.CAPTCHA_KEY;
import static org.springblade.modules.resource.utils.SmsUtil.*;
import static org.springblade.modules.resource.utils.SmsUtil.SEND_FAIL;

/**
 * App登录相关接口，需要引入User模块，暂不和EndPoint放在一起
 * @author: Sharry
 * @createTime: 2025-09-02 20:43
 * @version: Version-1.0
 */
@RestController
@AllArgsConstructor
@RequestMapping("/app/login")
@Api(value = "AppLoginController", tags = "App登录相关接口")
public class AppLoginController {

	private final IUserService userService;

	private final SmsBuilder smsBuilder;

	private final BladeRedis bladeRedis;

	@TenantIgnore
	@ApiLog("App账号密码登录")
	@PostMapping("/verify/password")
	@ApiOperationSupport(order = 1)
	@ApiOperation(value = "App账号密码登录", notes = "账号:account,密码:password")
	public R<List<AppUserTenantInfoVO>> verifyPassword(@RequestBody @Valid PasswordDTO passwordDTO){
		List<AppUserTenantInfoVO> appUserTenantInfoByPhoneAndPassword = userService.getAppUserTenantInfoByPhoneAndPassword(passwordDTO.getPhone(), DigestUtil.hex(passwordDTO.getPassword()));
		if(Func.isEmpty(appUserTenantInfoByPhoneAndPassword)){
			throw new ServiceException("账号或密码不正确或未加入通讯录！请检查后重试");
		}
		return  R.data(appUserTenantInfoByPhoneAndPassword);
	}

	@PostMapping("/oauth/token")
	@ApiOperationSupport(order = 2)
	@ApiLog("App账号密码登录与获取认证令牌")
	@ApiOperation(value = "获取认证令牌", notes = "传入租户ID:tenantId,账号:account,密码:password")
	public R<Kv> token(@ApiParam(value = "租户ID", required = true) @RequestParam String tenantId,
					   @ApiParam(value = "账号", required = true) @RequestParam(required = false) String username,
					   @ApiParam(value = "密码", required = true) @RequestParam(required = false) String password,
					   @ApiIgnore @RequestHeader(name = TokenUtil.DEPT_HEADER_KEY, required = false) String deptId,
					   @ApiIgnore @RequestHeader(name = TokenUtil.ROLE_HEADER_KEY, required = false) String roleId) {

		String grantType = WebUtil.getRequest().getParameter("grant_type");
		String refreshToken = WebUtil.getRequest().getParameter("refresh_token");

		String userType = Func.toStr(WebUtil.getRequest().getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);

		TokenParameter tokenParameter = new TokenParameter();
		tokenParameter.getArgs().set("tenantId", tenantId)
			.set("username", username)
			.set("password", password)
			.set("grantType", grantType)
			.set("refreshToken", refreshToken)
			.set("userType", userType)
			.set("deptId", deptId).set("roleId", roleId);

		ITokenGranter granter = TokenGranterBuilder.getGranter(grantType);
		UserInfo userInfo = granter.grant(tokenParameter);
		if (userInfo == null || userInfo.getUser() == null) {
			return R.fail(HttpServletResponse.SC_BAD_REQUEST, "用户名或密码不正确");
		}
		return R.data(TokenUtil.createAuthInfo(userInfo));
	}

	/**
	 * 根据手机号判断该用户是否离职、以及是否加入通讯录
	 * @param phone 手机号
	 */
	@TenantIgnore
	@GetMapping("/valid/phone")
	@ApiOperationSupport(order = 3)
	@ApiOperation(value = "根据手机号判断该用户是否离职、是否为有效账号以及是否加入通讯录")
	public R<List<AppUserTenantInfoVO>> validPhone(String phone) {
		List<AppUserTenantInfoVO> appTenantInfoByPhone = userService.getAppUserTenantInfoByPhone(phone);
		if(Func.isEmpty(appTenantInfoByPhone)){
			throw new ServiceException("手机号码还未加入通讯录，无法登录 \n 请联系公司负责人在通讯录添加您的信息");
		}
		return R.data(appTenantInfoByPhone);
	}

	/**
	 * @param phone 手机号
	 * @param operationType 操作类型: LOGIN, UPDATE_PASSWORD, FORGET_PASSWORD
	 */
	@SneakyThrows
	@PostMapping("/send/validate/code")
	@ApiOperationSupport(order = 4)
	@ApiOperation(value = "发送验证码")
	public R<SmsCode> sendValidate(@RequestParam String phone, @RequestParam String operationType) {
		if(Func.isBlank(phone)){
			throw new ServiceException("手机号不能为空");
		}
		Map<String, String> params = SmsUtil.getVerifyParams();
		SmsData smsData = new SmsData(params);
		smsData.setKey(VERIFY_CODE);
		SmsTemplate template = smsBuilder.template(APP_AUTH_CODE);
		boolean temp = template.sendSingle(smsData, phone);
		SmsCode smsCode = new SmsCode();
		if (temp && StringUtil.isNotBlank(smsData.getKey())) {
			String id = StringUtil.randomUUID();
			String value = smsData.getParams().get(smsData.getKey());
			// 用原本的阿里云逻辑验证码cacheKey
			String cacheKey = CAPTCHA_KEY + phone + StringPool.COLON + id;
			bladeRedis.setEx(cacheKey, value, Duration.ofMinutes(10));
			smsCode.setId(id).setValue(value);
		} else {
			smsCode.setSuccess(Boolean.FALSE);
			return R.fail(SEND_FAIL);
		}
//		SmsCode smsCode = smsBuilder.template(APP_AUTH_CODE).sendValidate(new SmsData(params).setKey(VERIFY_CODE), phone);
		bladeRedis.setEx("blade:sms::validate:"+smsCode.getId()+phone+operationType, smsCode.getValue(), Duration.ofMinutes(10L));
		return smsCode.isSuccess() ? R.data(smsCode, SEND_SUCCESS) : R.fail(SEND_FAIL);
	}

	/**
	 * 校验验证码是否过期
	 * 账号登录校验短信
	 * @param smsCode 短信校验信息
	 */
	@SneakyThrows
	@ApiOperationSupport(order = 5)
	@PostMapping("/get/validate/code")
	@ApiOperation(value = "短信校验信息")
	public R<?> loginValidateMessage(SmsCode smsCode, @RequestParam String operationType) {
		List<TenantInfoVO> tenantInfoByPhone = userService.getTenantInfoByPhone(smsCode.getPhone());
		if(Func.isEmpty(tenantInfoByPhone)){
			return R.fail("该手机号尚未注册!");
		}
		Object validateCode = bladeRedis.get("blade:sms::validate:"+smsCode.getId()+smsCode.getPhone()+operationType);
		if(Func.isEmpty(validateCode)){
			throw new ServiceException("验证码已过期，请重新获取");
		}
		boolean validate = smsBuilder.template().validateMessage(smsCode);
		// 特权账号(供小程序审核使用，不做验证码验证)
//		boolean isVipPhone = Arrays.binarySearch(VIP_PHONE,smsCode.getPhone()) >= 0;
		return validate ? R.data(tenantInfoByPhone) : R.fail(VALIDATE_FAIL);
	}

	/**
	 * 未登录态，修改当前用户租户密码，找回密码
	 */
	@TenantIgnore
	@SneakyThrows
	@ApiOperationSupport(order = 6)
	@PostMapping("/update/password/forget")
	@ApiOperation(value = "未登录态，修改当前用户租户密码，找回密码")
	public R<Boolean> updatePasswordForget(
									@ApiParam(value = "手机号", required = true) @RequestParam String phone,
									@ApiParam(value = "新密码", required = true) @RequestParam String newPassword){
		List<AppUserTenantInfoVO> appTenantInfoByPhone = userService.getAppUserTenantInfoByPhone(phone);
		if(Func.isEmpty(appTenantInfoByPhone)) {
			throw new ServiceException("手机号码还未加入通讯录，无法找回密码 \n 请联系公司负责人在通讯录添加您的信息");
		}
		// 修改该用户所有租户密码
		return R.data(userService.updateUserAllPassword(appTenantInfoByPhone, newPassword));
	}

	/**
	 * 已登录态，修改当前用户租户密码
	 * 新旧密码均需要前端传入前Md5加密
	 */
	@ApiOperationSupport(order = 7)
	@PostMapping("/update/password/by/token")
	@ApiOperation(value = "已登录态，修改当前用户租户密码")
	public R<Boolean> updatePassword(@RequestBody @Valid PasswordUpdateDTO passwordDTO){
		String newPassword = passwordDTO.getNewPassword();
		String oldPassword = passwordDTO.getOldPassword();
		Long userId = AuthUtil.getUserId();
		User currentUser = userService.getById(userId);
		if(Func.isEmpty(currentUser)){
			throw new ServiceException("用户不存在");
		}
		String phone = currentUser.getPhone();
		// 1. 先校验旧密码是否正确
		if(!DigestUtil.hex(oldPassword).equals(currentUser.getPassword())){
			throw new ServiceException("旧密码错误，请重新输入");
		}
		return R.data(userService.updatePasswordByPhone(phone, newPassword));
	}





}
